package com.example.mwavmedia.audio;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.TimedMetaData;
import android.text.TextUtils;
import android.util.Log;

import java.io.IOException;
import java.nio.ByteBuffer;

public class PcmDecoder {
    /**
     * /storage/emulated/0/Android/data/com.example.mwavlearndemo/files/folder_audio/encode.aac
     * 取出文件
     * 使用ffplay xxx.aac播放即可用本类测试
     */
    private static final String TAG = "PcmDecoder";

    private DecodeThread decodeThread;

    public void start(String path){
        stop();
        decodeThread = new DecodeThread(path,
                AudioManager.STREAM_MUSIC,
                44100,
                AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT,
                AudioTrack.MODE_STREAM);
        decodeThread.start();
    }
    public void stop(){
        if (decodeThread == null) return;
        decodeThread.stopDecode();
        decodeThread = null;
    }
    private static class DecodeThread extends Thread{
        private static final long TIMEOUT_MS = 2000L;
        private MediaExtractor mediaExtractor;
        private MediaCodec mediaCodec;
        private AudioTrack audioTrack;
        private String path;

        private final int streamType;
        private final int sampleRate;
        private final int channelConfig;
        private final int audioFormat;
        private final int mod;

        private int bufferSize;
        private MediaFormat format;
        private String mine;
        private boolean isStopDecode = false;

        public DecodeThread(String path,
                            int streamType,
                            int sampleRate,
                            int channelConfig,
                            int audioFormat,
                            int mod){
            this.path = path;
            this.streamType = streamType;
            this.sampleRate = sampleRate;
            this.channelConfig = channelConfig;
            this.audioFormat = audioFormat;
            this.mod = mod;
        }

        @Override
        public void run() {
            super.run();
            initMediaExtractor();
            initMediaCodec();
            initAudioTrack();
            decode();
        }
        private void initMediaExtractor(){
            if (TextUtils.isEmpty(path)) return;
            try {
                mediaExtractor = new MediaExtractor();
                mediaExtractor.setDataSource(path);

                int trackCount = mediaExtractor.getTrackCount();
                for (int i = 0; i < trackCount; i++) {
                    MediaFormat format = mediaExtractor.getTrackFormat(i);
                    String mine = format.getString(MediaFormat.KEY_MIME);
                    if (!TextUtils.isEmpty(mine) && mine.startsWith("audio/")){
                        mediaExtractor.selectTrack(i);
                        this.format = format;
                        this.mine = mine;
                        break;
                    }
                }
            }catch (IOException ioException){
                Log.e(TAG,"" + ioException.getMessage());
                mediaExtractor = null;
                format = null;
                mine = null;
            }
        }
        private void initMediaCodec(){
            if (TextUtils.isEmpty(mine) || format == null){
                return;
            }
            try {
                mediaCodec = MediaCodec.createDecoderByType(mine);
                mediaCodec.configure(format,null,null,0);
            }catch (IOException ioException){
                Log.e(TAG,"" + ioException.getMessage());
                mediaCodec = null;
            }
        }
        private void initAudioTrack(){
            bufferSize = AudioTrack.getMinBufferSize(sampleRate,channelConfig,audioFormat);
            audioTrack = new AudioTrack(streamType,sampleRate,channelConfig,audioFormat,bufferSize,mod);
        }
        private void decode(){
            if (mediaExtractor == null || mediaCodec == null || audioTrack == null) return;
            long startMs = System.currentTimeMillis();
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            mediaCodec.start();
            audioTrack.play();
            for(;;){
                if (isStopDecode){
                    release();
                    break;
                }
                int inputBufferId = mediaCodec.dequeueInputBuffer(TIMEOUT_MS);
                if (inputBufferId >= 0){
                    ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferId);
                    int readSize = -1;
                    if (inputBuffer != null){
                        readSize = mediaExtractor.readSampleData(inputBuffer,0);
                    }
                    if (readSize <= 0){
                        mediaCodec.queueInputBuffer(inputBufferId,0,0,0,
                                MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        isStopDecode = true;
                    }else {
                        mediaCodec.queueInputBuffer(inputBufferId,0,readSize,mediaExtractor.getSampleTime(),0);
                        mediaExtractor.advance();
                    }
                }
                int outputBufferId = mediaCodec.dequeueOutputBuffer(info, TIMEOUT_MS);
                if (outputBufferId >= 0){
                    ByteBuffer byteBuffer = mediaCodec.getOutputBuffer(outputBufferId);
                    if (byteBuffer != null && info.size > 0){
                        while (info.presentationTimeUs /1000 > System.currentTimeMillis() - startMs){
                            try {
                                sleep(10);
                            }catch (InterruptedException interruptedException){
                                interruptedException.printStackTrace();
                                break;
                            }
                        }
                        byte[] data = new byte[info.size];
                        byteBuffer.get(data);
                        byteBuffer.clear();
                        audioTrack.write(data,0,info.size);
                    }
                    mediaCodec.releaseOutputBuffer(outputBufferId,false);
                }
            }
        }
        void stopDecode(){
            isStopDecode = true;
            try {
                join(2000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        private void release(){
            if (mediaExtractor != null){
                mediaExtractor.release();
                mediaExtractor = null;
            }
            if (mediaCodec != null){
                mediaCodec.stop();
                mediaCodec.release();
                mediaCodec = null;
            }
            if (audioTrack != null){
                audioTrack.stop();
                audioTrack.release();
                audioTrack = null;
            }
            isStopDecode = true;
        }
    }
}
